package com.eprize;

public class Grid {

	private int numberOfRows;
	private int numberOfColumns;

	public boolean[][] cells;

	public void makeCellAlive(int row, int column) {
		cells[row][column] = true;
	}

	public void makeCellDead(int row, int column) {
		cells[row][column] = false;
	}

	public Grid(int numberOfRows, int numberOfColumns) {
		this.numberOfRows = numberOfRows;
		this.numberOfColumns = numberOfColumns;
		cells = new boolean[numberOfRows][numberOfColumns];
	}

	public int determineNumberOfLiveNeighbors(int rowIndex, int columnIndex) {
		int counter = 0;
		int startRow = rowIndex - 1;
		if (startRow < 0)
			startRow = 0;
		int startCol = columnIndex - 1;
		if (startCol < 0)
			startCol = 0;
		int endCol = columnIndex + 2;
		if (endCol > numberOfColumns)
			endCol = numberOfColumns;
		int endRow = rowIndex + 2;
		if (endRow > numberOfRows)
			endRow = numberOfRows;

		for (int rowPlace = startRow; rowPlace < endRow; rowPlace++) {
			for (int colPlace = startCol; colPlace < endCol; colPlace++) {
				if (cells[rowPlace][colPlace])
					if ((rowPlace != rowIndex) || (colPlace != columnIndex))
						counter++;
			}

		}
		return counter;
	}

	public boolean getCell(int row, int column) {
		return cells[row][column];
	}

	public void evolve() {
		boolean[][] futureState = deepClone(cells);
		for (int rowPlace = 0; rowPlace < numberOfRows; rowPlace++) {
			for (int colPlace = 0; colPlace < numberOfColumns; colPlace++) {
				int liveNeighborCount = determineNumberOfLiveNeighbors(
						rowPlace, colPlace);
				if ((liveNeighborCount == 2 && cells[rowPlace][colPlace])
						|| liveNeighborCount == 3)
					futureState[rowPlace][colPlace] = true;
				else
					futureState[rowPlace][colPlace] = false;
			}

		}
		cells = futureState;
	}

	/**
	 * Makes a copy of all dimensions of a multidimensional
	 * 
	 * array. This function does not copy the actual objects, but
	 * 
	 * only the references. The behavior is the same as {@link #clone()}
	 * 
	 * but extends to all dimensions of an array.<br>
	 * 
	 * Note that this implementation does not handle arrays which
	 * 
	 * contain themselves correctly. It results in a infinite recursion
	 * 
	 * ending in a {@link StackOverflowError}.
	 * 
	 * 
	 * 
	 * @param src
	 *            a multidimensional array, either of {@link Object}s
	 * 
	 *            or of a primitive type
	 * 
	 * @return a copy of src, so that each dimension is copied, but
	 * 
	 *         the objects are not cloned
	 * 
	 * @author Philipp Buluschek bulu@romandie.com
	 */

	@SuppressWarnings("unchecked")
	public static <T> T[] deepClone(T[] src) {
		if (src == null) {
			return null;
		}
		T[] dest = src.clone();
		for (int i = 0; i < dest.length; i++) {
			Object e = dest[i];
			if (e != null && e.getClass().isArray()) {
				// if it is null or not an array, it was taken care of by the
				// clone()
				if (e instanceof Object[]) {
					// using recursion to reach all dimensions
					dest[i] = (T) (deepClone((Object[]) e));
				} else {
					// primitive arr
					if (e instanceof byte[])
						dest[i] = (T) ((byte[]) e).clone();
					else if (e instanceof short[])
						dest[i] = (T) ((short[]) e).clone();
					else if (e instanceof int[])
						dest[i] = (T) ((int[]) e).clone();
					else if (e instanceof long[])
						dest[i] = (T) ((long[]) e).clone();
					else if (e instanceof float[])
						dest[i] = (T) ((float[]) e).clone();
					else if (e instanceof double[])
						dest[i] = (T) ((double[]) e).clone();
					else if (e instanceof boolean[])
						dest[i] = (T) ((boolean[]) e).clone();
				}
			}
		}
		return dest;
	}
}